const Koa = require('koa')
const fs = require('fs')
const path = require('path')
const app = new Koa()
const compilerSfc = require('@vue/compiler-sfc')
const compilerDom = require('@vue/compiler-dom')

app.use(async (ctx) => {
	const { url, query } = ctx.request
	let content
	// / => index.html
	if (url === '/') {
		content = helper('root', { ctx })
	}
	// *.js => /src/*.js
	if (url.endsWith('.js')) {
		content = helper('js', { ctx, url })
	}
	/* 
        第3方库的支持 
        /@modules/vue => /node_modules/vue es模块[package.json/module]
    */
	if (url.startsWith('/@modules')) {
		content = helper('@modules', { ctx, url })
	}
	/* 
        支持SFC 单文件组件
        .vue => 拆分 template script (@vue/compiler-sfc)
        template 模板 => render (@vue/compiler-dom)
        ~a = -(a+1)
    */
	if (~url.indexOf('.vue')) {
		content = helper('vue', { ctx, query, url })
	}
	/* 
        处理css
    */
	if (url.endsWith('.css')) {
		content = helper('css', { ctx, url })
	}

	ctx.body = content
})
/* 
    rewriteImport
    from 'vue' => from '/@modules/***'
*/
const rewriteImport = (content) => {
	return content.replace(/ from ['|"]([^'"]+)['|"]/g, (s0, s1) => {
		// console.log('s0 :>>',s0,'s1 :>>',s1);
		// 不是相对路径 ../ ./
		if (s1[0] !== '.') {
			return ` from '/@modules/${s1}' `
		}
		return s0
	})
}
const getType = (type = 'js') => {
	const map = {
		html: 'text/html',
		js: 'application/javascript',
	}
	return map[type]
}
const parseRoot = ({ ctx }) => {
	ctx.type = getType('html')
	let content = fs.readFileSync('./index.html', 'utf-8')
	//解决 process is not defined 加入环境变量
	return content.replace(
		'<script',
		`
        <script>
            window.process = { env:{ NODE_ENV : 'dev' } }
        </script>
        <script
    `
	)
}
const parseJavascript = ({ ctx, url }) => {
	ctx.type = getType()
	// /src/main.js
	// __dirname 当前运行的js文件所在的目录【绝对路劲】 => G:\wheel\mini-vite
	const _path = path.resolve(__dirname, url.slice(1))
	return rewriteImport(fs.readFileSync(_path, 'utf-8'))
}
const parseVue = ({ ctx, query, url }) => {
	// .vue?type=template
	const _path = path.resolve(__dirname, url.split('?')[0].slice(1))
	const {
		descriptor: { script, template },
	} = compilerSfc.parse(fs.readFileSync(_path, 'utf-8'))
	// query => {}
	ctx.type = getType()
	if (!query.type) {
		/* 
            descriptor.script => js + template 生成 render 
            借用vue自导的 compiler库 解析单文件组件 其实相当于 vue-loader 做的事情
        */
		return `
            ${rewriteImport(
				script.content.replace('export default ', ' const __script = ')
			)}
            import { render as __render } from "${url}?type=template"
            __script.render = __render
            export default __script
        `
	}
	const render = compilerDom.compile(template.content, { mode: 'module' })
	return rewriteImport(render.code)
}
const parseModules = ({ ctx, url }) => {
	const _path = path.resolve(
		__dirname,
		'node_modules',
		url.replace('/@modules/', '')
	)
	const { module } = require(_path + '/package.json')
	ctx.type = getType()
	// 第3方库里面还引用了其他的库
	return rewriteImport(fs.readFileSync(path.resolve(_path, module), 'utf-8'))
}
const parseCss = ({ ctx, url }) => {
	const _path = path.resolve(__dirname, url.slice(1))
	const file = fs.readFileSync(_path, 'utf-8')
	// css => js => style
	ctx.type = getType()
	return `
        const css = "${file.replace(/[\r\n]/g, '')}"
        const link = document.createElement("style")
        link.setAttribute("type","text/css")
        document.head.appendChild(link)
        link.innerHTML = css
        export default css
    `
}
const helper = (type, options) => {
	const map = {
		root: parseRoot,
		js: parseJavascript,
		vue: parseVue,
		'@modules': parseModules,
		css: parseCss,
	}
	return map[type]?.(options)
}
app.listen(3000, () => {
	console.log('Vite start at 3000 port')
})
